package cn.zniot.so.zniotcore;

import cn.zniot.so.application.constant.Constants;
import cn.zniot.so.application.util.MethodUtil;
import cn.zniot.so.transport.asynpack.AsynPacket;
import cn.zniot.so.transport.asynpack.LogicCotrolPacketD;
import cn.zniot.so.transport.asynpack.Zniot2ControlPacketD;
import cn.zniot.so.transport.exception.NetException;

import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.proxy.utils.ByteUtilities;

import java.io.UnsupportedEncodingException;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
import java.util.List;

/**
 * 盒子终端Zniot解码器
 */
public class ZniotDecoder extends CumulativeProtocolDecoder {
	private static Logger logger = Logger.getLogger(ZniotDecoder.class);

	// 未处理字节和未处理字节长度（TCP传输中出现的不完整报文）
	// 有错：两处，第一次 单例比较忌讳类属性(全局变量),第二，mina已经帮你处理断包了 jacarrichan
	private byte[] remain = new byte[1024];
	private int remainlen = 0;
	private byte[] data;
	// 数据包过大时的提示
	private static byte[] bigPacket = { 0x6, 0x5, 0x5, 0x3, 0x5 };
	private String ipAndPort;

	/*
	 * @覆盖父类或实现接口的doDecode（IoSession session, IoBuffer in,ProtocolDecoderOutput
	 * out）方法 方法名：doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput
	 * out) 功　能：将二进制数据转换成高层数据(对象) 参　数：IoSession session -&盒子跟服务器之间建立的连接会话,
	 * IoBuffer in -&提供对字符串类型对象的操作,ProtocolDecoderOutput out
	 * -&把成功解码的数据传输给iohandler层 返回值：boolean - &解码是否成功 参　考：@see
	 * org.apache.mina.filter
	 * .codec.CumulativeProtocolDecoder#doDecode(org.apache
	 * .mina.core.session.IoSession, org.apache.mina.core.buffer.IoBuffer,
	 * org.apache.mina.filter.codec.ProtocolDecoderOutput)
	 * 
	 * ====================<br>
	 */
	// * 已用该条数据测试 :AA AA 00 12 17 23 34 12 23 34 12 23 34 12 23 34 12 23 34 12
	// 23 34 00 00 5A 5A <br>
	// * 帧头 AA AA <br>
	// * 数据长度 00 12 <br>
	// * 命令头 17<br>
	// * 命令体 23 34 12 23 34 12 23 34 12 23 34 12 23 34 12 23 34<br>
	// * CRC 00 00<br>
	// * 帧尾 5A 5A <br>
	@Override
	protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) {
		boolean isvlidate = true;
		// Remember the initial position.
		int start = in.position();
		// Now find the first CRLF in the buffer.
		byte previous = 0;
		while (in.hasRemaining()) {
			byte current = in.get();
			// 如果收到一个包尾
			if ((previous == (byte) 0x5A) && (current == (byte) 0x5A)) {
				// Remember the current position and limit.
				int position = in.position();// 取得当前位置,也就是current所在的位置
				int limit = in.limit();
				logger.debug(position + "   " + limit);
				try {
					in.position(start);// 把位置置为开始,好让后面开始从这里读取数据
					in.limit(position);// 把限制置为包尾位置,好让后面从读取从start到限制位的位置
					// =================================
					byte headPrevious = 0;
					byte headCurrent = 0;
					// 寻找包头AA AA
					headCurrent = in.get();
					while (!(headPrevious == (byte) 0xAA && (headCurrent == (byte) 0xAA))) {
						headPrevious = headCurrent;
						headCurrent = in.get();
						// 【最后一个5A的位置】减去【第二个AA】的位置,得出包体的长度如果包体字节数超过65535，直接抛弃
						if ((position - in.position()) > 65535) {
							logger.debug("Package len must less than  65535");
							// ========================
							session.write(bigPacket);
							// ========================
							isvlidate = false;
							in.position(position);
							break;
						}
					}
					if (isvlidate) {
						// 2字节的长度,先取高字节再取低字节
						int height = in.get();
						int low = in.get();
						int packageLen = height * (16 * 16) + low;
						logger.debug("packageLen " + packageLen + "   " + height + "  " + low);
						// //====业务数据包 (命令头,命令体)==============
						// content
						byte[] content = new byte[packageLen];
						in.get(content, 0, content.length);
						// //======帧校验 CRC============
						byte[] check = new byte[2];
						in.get(check, 0, check.length);
						// //======帧尾 [0xaa 0xaa]============
						byte[] tail = new byte[2];
						in.get(tail, 0, tail.length);
						// if (checkCRC(check, content)) {
						// 现在不管CRC哟
						if (true) {
							// 心跳包是一个空包
							if (0 == content.length) {
								byte[] heartRequest = { 0x5a, 0x5a, (byte) 0xaa, (byte) 0xaa };
								content = heartRequest;
							}
							// 上层会收到命令头和命令体或者心跳包
							logger.debug("content.." + ByteUtilities.asHex(content));
							out.write(content);

						} else {
							logger.warn("CRC校验失败");
						}
					}
				} catch (BufferUnderflowException e) {
					// 包数据有误时,(断包,乱码)直接抛弃
					e.printStackTrace();
				} finally {
					// Set the position to point right after the
					// detected line and set the limit to the old
					// one.
					in.position(position);
					in.limit(limit);
				}
				// Decoded one line; CumulativeProtocolDecoder will
				// call me again until I return false. So just
				// return true until there are no more lines in the
				// buffer.
				return true;
			}
			previous = current;
		}

		// Could not find CRLF in the buffer. Reset the initial
		// position to the one we recorded above.
		in.position(start);

		return false;

		// byte[] data = new byte[in.remaining()];// IoBuffer中还存在的元素个数
		// IoBuffer ioBuffer = in.get(data, 0, data.length);//
		// 从IoBuffer中取到数据放入数组data中，data数组中存的是ASCII码值
		// try
		// {
		// List<AsynPacket> list = this.decode(data, session);
		//
		// if (list.size() > 0)
		// {
		// for (AsynPacket asynPacket : list)
		// {
		// out.write(asynPacket);// 传递报文到handler层
		// }
		// return true;
		// }
		// else
		// {
		// return false;
		// }
		// } catch (Exception e)
		// {
		// logger.error(e.getMessage());
		// return false;
		// }
	}

	/**
	 * 解码Zniot协议
	 */
	private List<AsynPacket> decode(byte[] data, IoSession session) throws NetException, Exception {
		// Zniot上传有可能一下上传多个甚至包括半个包数据 TODO TCP的粘滞性
		this.data = data;
		List<AsynPacket> lstPackets = null;
		AsynPacket pAsynPacket = null;
		String thisMessage = null;
		try {
			ipAndPort = session.getRemoteAddress().toString();
			lstPackets = new ArrayList<AsynPacket>(1);
			try {
				thisMessage = new String(data, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				logger.error(MethodUtil.printExceptionErr(e));
				return lstPackets;
			}
			// if (thisMessage.equals(Constants.HEART_BEAT_UP))
			// {
			// // 客户端发的心跳
			// if (logger.isDebugEnabled()) {
			// logger.debug(ipAndPort+"|给服务器回复的心跳--" + thisMessage);
			// }
			// }
			if (thisMessage.contains(Constants.DEFAUT_VALUE)) {
				if (logger.isDebugEnabled()) {
					logger.debug("盒子给服务器回复的内容(B-login)--" + thisMessage);
				}
				// 处理客户端上线或者重连的现象
				pAsynPacket = processLoginMessage(thisMessage);
				if (pAsynPacket != null) {
					lstPackets.add(pAsynPacket);
				}
			}
			// 返回成功还是失败的日志
			else if (data.length == 7 && data[0] == 0x23) {
				return controlCommand(data, lstPackets, session);
			} else if (thisMessage.contains(Constants.ID_HEAD)) {
				// 认为是手机端发过来的东西
				if (logger.isDebugEnabled()) {
					logger.debug("手机给服务器回复的内容(M-login)---" + thisMessage);
				}
				// 处理客户端上线或者重连的现象
				pAsynPacket = processLoginMessage(thisMessage);
				if (pAsynPacket != null) {
					lstPackets.add(pAsynPacket);
				}
			}// 控制命令的执行操作
			else if (data.length == 12 && data[3] == (byte) 0xEA) {
				return controlCommand(data, lstPackets, session);
			}
		} catch (Exception e) {
			logger.error(MethodUtil.printExceptionErr(e));
		}
		return lstPackets;
	}

	private List<AsynPacket> controlCommand(byte[] data, List<AsynPacket> lstPackets, IoSession session) {
		AsynPacket pAsynPacket;
		if (logger.isDebugEnabled()) {
			// 以16进制格式输出接收到的所有数据
			if (logger.isInfoEnabled()) {
				logger.info("Receiving " + ipAndPort + "/" + Constants.PROTOCOL_VERSION + " packet->"
						+ MethodUtil.Hex2String(data));
			}
		}
		Zniot2ControlPacketD Zniot2p = null;
		if (Zniot2p == null) {
			Zniot2p = new Zniot2ControlPacketD();
		}
		Zniot2p.setCode(data);
		pAsynPacket = (AsynPacket) Zniot2p;
		if (pAsynPacket != null) {
			lstPackets.add(pAsynPacket);
		}
		return lstPackets;
	}

	public AsynPacket processLoginMessage(String thisMessage) {
		AsynPacket lstAsynPacket = processControlMessageReturn(thisMessage);
		return lstAsynPacket;
	}

	/**
	 * 逻辑控制命令返回
	 */
	private static AsynPacket processControlMessageReturn(String thisMessage) {
		LogicCotrolPacketD p = new LogicCotrolPacketD();
		try {
			// 如: #AI+1234,1,ReC,863070010228970!&010C
			String arr[] = thisMessage.split("\\!")[0].split("\\+");
			String type = arr[0].replace("#", "");
			p.setType(type);
			if (arr.length > 1) {
				String contents = arr[1];
				String arr2[] = contents.split(",");
				int dataLen = arr2.length;
				if (dataLen == 1) {
				} else if (dataLen == 2) {
					p.setData1(arr2[1]);// 查询当前版本返回指令
				} else if (dataLen == 3) {
					// 设置DUMMY间隔指令
					p.setData1(arr2[1]);
					p.setData2(arr2[2]);
				} else if (dataLen == 4) {
					// 主动上报密码和IMEI指令
					p.setData1(arr2[1]);
					p.setData2(arr2[2]);
					p.setImei(arr2[3]);
					p.setData5(arr2[0]);
				} else if (dataLen == 6) {
					p.setData5(arr2[5]);
				}
			}
		} catch (Exception e) {
			logger.error("处理逻辑控制命令返回出错!");
			logger.error(MethodUtil.printExceptionErr(e));
		}
		return p;
	}
}
